﻿using System;
using System.Security.Principal;
using System.Web;
using System.Web.Security;
using System.Xml;

namespace YanZhiwei.DotNet2.Utilities.AspNet
{
    /// <summary>
    /// Form验证帮助类
    /// </summary>
    public class FormsAuthenHelper
    {
        /* 访问授权:设置是在你要控制的那个目录下的web.config文件中来设置.
         <authorization>
           <allow users="comma-separated list of users"
               roles="comma-separated list of roles"
               verbs="comma-separated list of verbs" />
            <deny users="comma-separated list of users"
               roles="comma-separated list of roles"
               verbs="comma-separated list of verbs" />
         </authorization>
        <allow>标签表示允许访问，其中的属性
       1. users：一个逗号分隔的用户名列表，这些用户名已被授予对资源的访问权限。问号 (?) 允许匿名用户；星号 (*) 允许所有用户。
       2. roles：一个逗号分隔的角色列表，这些角色已被授予对资源的访问权限。
       3. verbs：一个逗号分隔的 HTTP 传输方法列表，这些 HTTP 传输方法已被授予对资源的访问权限。注册到 ASP.NET 的谓词为 GET、HEAD、POST 和 DEBUG。
       */
        #region 创建验证票据
        /// <summary>
        /// 创建验证票据
        /// </summary>
        /// <param name="userName">与身份验证票关联的用户名。</param>
        /// <param name="userData">将存储在 Cookie 中的用户定义数据</param>
        /// <param name="createPersistentCookie">如果 Cookie 是持久的，为 true；否则为 false。</param>
        /// <param name="cookiePath">存储 Forms 身份验证票证信息的 Cookie 的路径。默认值是“/”。</param>
        /// <returns>FormsAuthenticationTicket</returns>
        private static FormsAuthenticationTicket CreateAuthenticationTicketTool(string userName, string userData, bool createPersistentCookie, string cookiePath)
        {
            string _cookiePath = string.IsNullOrEmpty(cookiePath) == true ? FormsAuthentication.FormsCookiePath : cookiePath;
            int _expirationMinutes = GetCookieTimeout(); //获取cookie超时时间
            //创建验证票据
            FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
            1,                      //版本号
            userName,               //与身份验证票关联的用户名。 
            DateTime.Now,           //Cookie 的发出时间。 
            DateTime.Now.AddMinutes(_expirationMinutes), //Cookie 的到期日期。
            createPersistentCookie, //如果 Cookie 是持久的，为 true；否则为 false。
            userData,    //将存储在 Cookie 中的用户定义数据
            _cookiePath); //存储 Forms 身份验证票证信息的 Cookie 的路径。默认值是“/”。
            /*
             * 设置cookiePath
             <authentication mode="Forms">
             <forms loginUrl="member_login.aspx"
             cookieless="UseCookies"
             path="/MyApplication" />
             </authentication>
             */

            /*
             <authentication mode="Forms">
             <forms
                 name=".ASPXAUTH"//Cookie的名字。Forms Authentication可能会在验证后将用户凭证放在Cookie中，name属性决定了该Cookie的名字。
                 loginUrl="login.aspx"//登录页的URL。通过FormsAuthentication.LoginUrl属性可以得到该配置值。
                                        当调用FormsAuthentication.RedirectToLoginPage()方法时，客户端请求将被重定向到该属性所指定的页面。
                                        loginUrl的默认值为“login.aspx”，这表明即便不提供该属性值，ASP.NET也会尝试到站点根目录下寻找名为login.aspx的页面。
                 defaultUrl="default.aspx"//默认页的URL。通过FormsAuthentication.DefaultUrl属性得到该配置值。
                 protection="All"//Cookie的保护模式，可取值包括All（同时进行加密和数据验证）、Encryption（仅加密）、
                                   Validation（仅进行数据验证）和None。为了安全，该属性通常从不设置为None。
                 timeout="30" //Cookie的过期时间。
                 path="/" //Cookie的路径。可以通过FormsAuthentication.FormsCookiePath属性得到该配置值。
                 requireSSL="false" //在进行Forms Authentication时，与服务器交互是否要求使用SSL。可以通过FormsAuthentication.RequireSSL属性得到该配置值。
                 slidingExpiration="false" //是否启用“弹性过期时间”，如果该属性设置为false，
                                             从首次验证之后过timeout时间后Cookie即过期；如果该属性为true，
                                             则从上次请求该开始过timeout时间才过期，这意味着，在首次验证后，
                                             如果保证每timeout时间内至少发送一个请求，则Cookie将永远不会过期。
                                             通过FormsAuthentication.SlidingExpiration属性可以得到该配置值。
                 enableCrossAppRedirects="false" //是否可以将以进行了身份验证的用户重定向到其他应用程序中。
                                                   通过FormsAuthentication.EnableCrossAppRedirects属性可以得到该配置值。
                                                   为了安全考虑，通常总是将该属性设置为false。
                 cookieless="UseDeviceProfile" //定义是否使用Cookie以及Cookie的行为。
                                                 Forms Authentication可以采用两种方式在会话中保存用户凭据信息，一种是使用Cookie，
                                                 即将用户凭据记录到Cookie中，每次发送请求时浏览器都会将该Cookie提供给服务器。
                                                 另一种方式是使用URI，即将用户凭据当作URL中额外的查询字符串传递给服务器。
                                                 该属性有四种取值——UseCookies（无论何时都使用Cookie）、
                                                 UseUri（从不使用Cookie，仅使用URI）、AutoDetect（检测设备和浏览器，只有当设备支持Cookie并且在浏览器中启用了Cookie时才使用Cookie）
                                                 和UseDeviceProfile（只检测设备，只要设备支持Cookie不管浏览器是否支持，都是用Cookie）。
                                                 通过FormsAuthentication.CookieMode属性可以得到该配置值。
                                                通过FormsAuthentication.CookiesSupported属性可以得到对于当前请求是否使用Cookie传递用户凭证。
                 domain="" //Cookie的域。通过FormsAuthentication.CookieDomain属性可以得到该配置值。
             />
             </authentication>
             */

            /*
             <authorization> 
              <deny users="?" />
              <allow users="*" />
            </authorization>
             */

            /*    
               <authentication mode="Forms">
              <forms name=".ASPXAUTH"
              defaultUrl="default.aspx"
              protection="All"
              timeout="30"
              path="/"
              requireSSL="false"
              slidingExpiration="false"
              enableCrossAppRedirects="false"
              cookieless="UseDeviceProfile"
              domain=""
              />
            </authentication>
             */
            return ticket;
        }
        private static void SetAuthCookieTool(string userName, string userData, bool createPersistentCookie, string cookiePath)
        {
            FormsAuthenticationTicket _ticket = CreateAuthenticationTicketTool(userName, userData, createPersistentCookie, cookiePath);
            string _encrypetedTicket = FormsAuthentication.Encrypt(_ticket);//加密票据

            if (!FormsAuthentication.CookiesSupported)//如果应用程序已配置为支持无 Cookie 的 Forms 身份验证，则返回 true；否则返回 false。 
            {
                FormsAuthentication.SetAuthCookie(_encrypetedTicket, createPersistentCookie);//将验证信息,存放在Uri中
            }
            else
            {
                HttpCookie _authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, _encrypetedTicket);
                if (_ticket.IsPersistent)
                    _authCookie.Expires = _ticket.Expiration;
                HttpContext.Current.Response.Cookies.Add(_authCookie);
            }
        }
        private static void RedirectFromLoginPageTool(string userName, string userData, bool createPersistentCookie, string cookiePath)
        {
            SetAuthCookieTool(userName, userData, createPersistentCookie, cookiePath);
            HttpContext.Current.Response.Redirect(FormsAuthentication.GetRedirectUrl(userName, createPersistentCookie));
        }
        #endregion
        #region 设置登录Cookie凭证
        /// <summary>
        /// 设置登录Cookie凭证
        /// </summary>
        /// <param name="userName">用户名称</param>
        /// <param name="userData">用户数据</param>
        /// <param name="createPersistentCookie">是否是持久cookie</param>
        public static void SetAuthCookie(string userName, string userData, bool createPersistentCookie)
        {
            SetAuthCookieTool(userName, userData, createPersistentCookie, null);
        }
        /// <summary>
        /// 设置登录Cookie凭证
        /// </summary>
        /// <param name="userName">用户名称</param>
        /// <param name="userData">用户数据</param>
        /// <param name="createPersistentCookie">是否是持久cookie</param>
        /// <param name="cookiePath">cookie路径</param>
        public static void SetAuthCookie(string userName, string userData, bool createPersistentCookie, string cookiePath)
        {
            SetAuthCookieTool(userName, userData, createPersistentCookie, cookiePath);
        }
        #endregion
        #region 从登录页面跳转
        /// <summary>
        /// 从登录页面跳转
        /// </summary>
        /// <param name="userName">用户名称</param>
        /// <param name="userData">用户数据</param>
        /// <param name="createPersistentCookie">是否是持久cookie</param>
        /// <param name="cookiePath">cookie路径</param>
        public static void RedirectFromLoginPage(string userName, string userData, bool createPersistentCookie, string cookiePath)
        {
            RedirectFromLoginPageTool(userName, userData, createPersistentCookie, cookiePath);
        }
        /// <summary>
        /// 从登录页面跳转
        /// </summary>
        /// <param name="userName">用户名称</param>
        /// <param name="userData">用户数据</param>
        /// <param name="createPersistentCookie">是否是持久cookie</param>
        public static void RedirectFromLoginPage(string userName, string userData, bool createPersistentCookie)
        {
            RedirectFromLoginPageTool(userName, userData, createPersistentCookie, string.Empty);
        }
        #endregion
        #region 获取cookie超时时间
        /// <summary>
        /// 获取cookie超时时间
        /// </summary>
        /// <returns>超时时间</returns>
        private static int GetCookieTimeout()
        {
            int _defaultTimeout = 30; //Default timeout is 30 minutes
            XmlDocument _webConfig = new XmlDocument();
            _webConfig.Load(HttpContext.Current.Server.MapPath(@"~\web.config"));
            XmlNode _node = _webConfig.SelectSingleNode("/configuration/system.web/authentication/forms");
            if (_node != null && _node.Attributes["timeout"] != null)
            {
                if (!int.TryParse(_node.Attributes["timeout"].Value, out _defaultTimeout))
                    _defaultTimeout = 30;
            }
            return _defaultTimeout;
        }
        #endregion
        #region 用户权限初始化
        /// <summary>
        /// 用户权限初始化 在 Application_AuthenticateRequest()中使用
        /// </summary>
        /// <param name="assignRoleRule">委托</param>
        public static void InitRolesToUser(Func<string, string[]> assignRoleRule)
        {
            if (HttpContext.Current.User == null) return;
            if (!HttpContext.Current.User.Identity.IsAuthenticated) return;

            if (HttpContext.Current.User.Identity is FormsIdentity)
            {
                FormsIdentity _id = (FormsIdentity)HttpContext.Current.User.Identity;
                FormsAuthenticationTicket _ticket = _id.Ticket;

                if (!FormsAuthentication.CookiesSupported)
                    _ticket = FormsAuthentication.Decrypt(_id.Ticket.Name);

                if (!string.IsNullOrEmpty(_ticket.UserData))
                {
                    string _userData = _ticket.UserData;
                    string[] _roles = assignRoleRule != null ? assignRoleRule(_userData) : _userData.Split(',');
                    if (_roles != null && _roles.Length > 0)
                        HttpContext.Current.User = new GenericPrincipal(_id, _roles);
                    //Principal objects对象(比如GenericPrincipal)提供了用户身份(identity)以及所属角色的信息。这样当前用户就拥有了role信息 .     
                }
            }
        }
        #endregion
    }
}
